/*
    This file is part of QTau
    Copyright (C) 2013-2018  Tobias "Tomoko" Platen <tplaten@posteo.de>
    Copyright (C) 2013       digited       <https://github.com/digited>
    Copyright (C) 2010-2013  HAL@ShurabaP  <https://github.com/haruneko>

    QTau is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    SPDX-License-Identifier: GPL-3.0+
*/

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QDir>
#include <QMap>
#include <QObject>
#include <QThread>
#include "Utils.h"

class MainWindow;
class qtauSynth;
class qtmmPlayer;
class qtauAudioSource;
class qtauSession;
class AudioEngine;
class JackAudio;
#include "PluginInterfaces.h"
#include "audio/outputbuffer.h"

// main class of QTau that ties everything together, also used in headless mode
// (TODO)
class qtauController : public QObject, public IController {
    Q_OBJECT
public:
    explicit qtauController(QString dir, QObject* parent = 0);
    ~qtauController();
    void shutdown(int rc);  // must be called before destoying the object

    bool run();  // app startup & setup, window creation
    ISynth* activeSynth() {
        return _synth;
    }
    ISynth* selectedSynth() {
        return _synth;
    };
    static qtauController* instance();

    void startPlayback(float startPos);
    void stopPlayback();
    void selectSinger(QString singerName);
    void updateTempoTimeSignature(int tempo);
    int sampleRate();
    void startOfflinePlayback(const QString& fileName);
    QString lastPlay() {
        return _lastPlay;
    }

signals:

    void playStart();
    void playPause();
    void playStop();

    void playerSetVolume(int level);
    void transportPositionChanged(float pos);
    void voiceListChanged();

public slots:
    void onLoadUST(QString fileName);
    void onSaveUST(QString fileName, bool rewrite);

    void onRequestStartPlayback();
    void onRequestStopPlayback();
    void onRequestResetPlayback();

    void pianoKeyPressed(int);
    void pianoKeyReleased(int);

    void openVoiceDirectory();
    void rescanVoiceDirectory();

private slots:
    void jackTimer();
    void outbuf_startPlayback();
    void outbuf_stopPlayback();

private:
    // void transportStarting();
    void addFileToRecentFiles(QString fileName);
    bool validateScore(const QJsonArray& ust);

protected:
    JackAudio* _jack = nullptr;
    AudioEngine* _audio = nullptr;
    OutputBuffer* _outbuf = nullptr;
    MainWindow* _mainWindow;

    qtauSession* _activeSession;
    // float _samplesToMeasures;

    bool setupTranslations();
    bool setupPlugins();
    bool setupVoicebanks();

    void initSynth(ISynth* s);
    void initPreviewSynth(IPreviewSynth* ps);
    QMap<QString, ISynth*> _synths;
    // FIXME: synth selection
    ISynth* _synth = nullptr;
    // ISynth* _activeSynth=nullptr;
    // ISynth* _selectedSynth=nullptr;
    int _nonzeroStart = 0;

    QDir _pluginsDir;
    QString _prefix;
    // QStringList _voices;
    QMap<QString, QString> _voicesMap;

    bool _needDownload=false;

    bool _previewRunning = false;
    bool _localRequestStartPlayback = false;
    IPreviewSynth* _preview = nullptr;
    QString _lastPlay;

    float _lastNoteEnd = 0;
    int _jackSampleRate = 0;

    void newEmptySession();
    void logError(const QString& error);
    void logDebug(const QString& debug);
    void logSuccess(const QString& success);
    void addPluginAction(QAction* action);

public:
    const QStringList voices() {
        return _voicesMap.keys();
    }
    void setJackTranportEnabled(bool enabled);
    bool needDownload() { return _needDownload; }
};

#endif  // CONTROLLER_H
